二、嵌入式开发编译工具链对比
(a)嵌入式开发的编译工具链是什么?
现在有这么一个场景:你手头上有一个单片机,你需要让它运行你编写的程序。那么,如何让单片机运行你写的程序呢?
这就会用到嵌入式开发当中的“编译工具链”。所谓编译工具链,其工作流程如下:
- 编写与转换:将你通过代码编辑器编写的原始代码,通过编译工具编译为可执行文件。这些可执行文件通常是二进制(Binary)或 Hex 格式。
- 烧录程序:
- 将编译生成的 Hex 或者二进制文件烧录到单片机中。至于具体如何烧录,通常有以下两种方式:
- 通用方式:使用 USB 数据线将单片机和电脑连接在一起
- STM32 专用方式:需要使用 ST-Link 作为中间介质,来连接单片机和电脑
之所以需要这个过程,是因为单片机是“裸机运行”的。裸机运行意味着:
- 定义:指程序直接运行在硬件上,没有任何操作系统(包括 Linux、Windows、甚至 RTOS)的介入。
- 直接操作硬件:这些可执行文件会直接操作单片机内部的寄存器。
- 寄存器原理:寄存器就类似于家里的电灯开关,按一下就开,按一下就关。每一个操作寄存器的动作,本质上就是在控制这些“开关”。
从源头的代码到最终的可执行文件,本质上就是通过程序逻辑不停地去触发这些寄存器开关。通过这种方式,单片机就能按照你所编写的程序,实现预期的效果。
(a.1)综合对比表:
举个例子:
假设你是一个英俊的小伙子或者漂亮的姑娘,现在从国外回来。男生带了女朋友,女生带了男朋友。你们从机场打车回家,回到了爸妈为你新盖的、面积 500 平的三层大别墅。
现在屋里漆黑一片,你进门后首先要打开第一个大灯。你不能去二楼打开,因为你不会飞,只能在一楼开灯。
由此可以类比单片机的工作原理:
- 可执行文件的存储地址: 单片机里的可执行文件需要下载到一个固定的地方,也就是“地址”。这第一个大灯的地址,就是可执行文件开始的地方,所有单片机的程序都会从这个地址开始执行。
- 寄存器操作: 按下大灯开关的行为,本质上就是对“寄存器”进行操作。通过操作寄存器,你把开关打开了。
- 可执行文件与寄存器的关系: 单片机最主要的工作就是操作这些寄存器(把寄存器理解为电灯的开关)。把所有的灯打开,实际上就是将寄存器里所有的“0”设置为“1”。
当然,后续我们还会详细讲解:
- 上拉电阻与下拉电阻
- 开漏输出等相关内容
至于“0”代表关还是开,“1”代表开还是关,这些细节我们留到后面再去详细解释。
(b)嵌入式开发编译工具链的实操流程

(b.1)以 ARM 嵌入式开发为例,一个典型的 交叉编译工具链(如 arm-none-eabi-gcc)包含:
(c)纯软件开发和嵌入式开发具体区别?
说到纯软件开发,这里有一个比较直观的例子就是 Python。Python 是一款高级的、解释性的编程语言,它的代码在执行过程中是边解释边执行的。
更直观地说:
- 你在 IDE(集成开发环境)里面编写好代码后,点击“立即执行”
- 在执行过程中,你可以在 Windows 或 Linux 电脑上,通过 Python 将需要的结果 print(打印)出来
- 最终你想要的结果会直接显示在电脑屏幕上
相对于单片机嵌入式开发来说,情况则完全不同:
- 单片机没有任何系统可以依赖,是“裸机运行”的
- 你的代码不能直接运行在单片机上,而是要先通过 C 语言的编译工具,将编写好的源代码翻译成一系列二进制文件(即 0 和 1,代表电路的开和关)
- 只有有了这些二进制指令,单片机内部的寄存器才知道需要执行什么操作,从而运行你的代码
Python 代码示例:
plain
# hello.py
print("Hello, World!")C 语言代码为例:
plain
// main.c
#include "stm32f4xx.h"
int main(void) {
// 配置 GPIO 点亮 LED
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
GPIOA->MODER |= GPIO_MODER_MODER5_0;
GPIOA->ODR |= GPIO_ODR_OD5;
while(1);
}